﻿using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;

namespace Piao
{

    /// <summary>
    /// HttpHelper class
    /// </summary>
    public class WebUtil
    {
        const string @refUrl = "https://dynamic.12306.cn/otsweb/";

        public string Post(string url, string content)
        {
            HttpStatusCode code = HttpStatusCode.NotFound;
            return Post(url, content, @refUrl, null, out code);
        }


        public string Post(string url, string content, string referer, Dictionary<string, string> headers, out HttpStatusCode code)
        {
            int tries = MAX_RETRY;

            int httpTimeout = TIMEOUT;
            code = 0;
            while (tries-- > 0)
            {
                try
                {
                    RemoteCertificateValidationCallback del = (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) => { return true; };
                    ServicePointManager.ServerCertificateValidationCallback = del;

                    HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(new Uri(url));
                    httpRequest.UserAgent = USERAGENT;
                    httpRequest.CookieContainer = cookie;
                    httpRequest.Referer = referer;
                    httpRequest.Headers.Add("Accept-Language", "zh-CN");
                    if (headers != null)
                    {
                        foreach (var pair in headers)
                        {
                            httpRequest.Headers.Add(pair.Key, pair.Value);
                        }
                    }

                    byte[] buff = Encoding.UTF8.GetBytes(content);
                    httpRequest.Method = "POST";
                    httpRequest.Timeout = httpTimeout;
                    httpRequest.ContentType = "application/x-www-form-urlencoded";
                    httpRequest.ContentLength = buff.Length;
                    if (proxy != null)
                        httpRequest.Proxy = proxy;

                    Stream reqStream = httpRequest.GetRequestStream();
                    reqStream.Write(buff, 0, buff.Length);
                    reqStream.Close();

                    HttpWebResponse res = (HttpWebResponse)httpRequest.GetResponse();
                    Stream resStream = null;
                    //string contentType = res.ContentType; //use utf8 only
                    string contentCode = res.ContentEncoding;

                    if (contentCode.Contains("gzip"))
                    {
                        resStream = new System.IO.Compression.GZipStream(
                                                    res.GetResponseStream(),
                                                    System.IO.Compression.CompressionMode.Decompress);

                    }
                    else
                        resStream = res.GetResponseStream();

                    StreamReader sr = new StreamReader(resStream, Encoding.UTF8);
                    code = res.StatusCode;
                    string fullResponse = sr.ReadToEnd();

                    SetCookiesForBrowser(url);

                    return fullResponse;

                }
                catch (Exception e)
                {
                    Failure++;
                    httpTimeout += 5;
                    Log.Error(e.Message);
                }
            }

            return string.Empty;
        }


        public string Get(string url)
        {
            return Get(url, @refUrl);
        }

        /// <summary>
        /// Get data from server
        /// </summary>
        /// <param name="url"></param>
        /// <returns>Return content</returns>
        public string Get(string url, string referer)
        {
            int tries = MAX_RETRY;
            int httpTimeout = TIMEOUT;
            while (tries-- > 0)
            {
                try
                {
                    HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(new Uri(url));
                    httpRequest.UserAgent = USERAGENT;
                    httpRequest.CookieContainer = cookie;
                    httpRequest.Referer = referer;
                    httpRequest.Headers.Add("Accept-Language", "zh-CN");
                    httpRequest.Method = "GET";
                    httpRequest.Timeout = httpTimeout;
                    if (proxy != null)
                        httpRequest.Proxy = proxy;

                    HttpWebResponse res = (HttpWebResponse)httpRequest.GetResponse();
                    StreamReader sr = new StreamReader(res.GetResponseStream(), Encoding.UTF8);
                    string fullresponse = sr.ReadToEnd();

                    SetCookiesForBrowser(url);
                    return fullresponse;

                }
                catch (Exception e)
                {
                    Failure++;
                    httpTimeout += 5;
                    Log.Error(e.Message);
                }
            }

            return string.Empty;
        }

        public System.Drawing.Image DownloadCaptcha(string url)
        {
            int tries = MAX_RETRY;
            int httpTimeout = TIMEOUT;
            while (tries-- > 0)
            {
                try
                {
                    HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(new Uri(url));
                    httpRequest.UserAgent = USERAGENT;
                    httpRequest.CookieContainer = cookie;
                    httpRequest.Referer = refUrl;

                    httpRequest.Method = "GET";
                    httpRequest.Timeout = TIMEOUT;
                    if (proxy != null)
                        httpRequest.Proxy = proxy;

                    HttpWebResponse res = (HttpWebResponse)httpRequest.GetResponse();

                    var image = System.Drawing.Image.FromStream(res.GetResponseStream());

                    SetCookiesForBrowser(url);

                    return image;
                }
                catch (Exception e)
                {
                    httpTimeout += 5;
                    Log.Error(e.Message);
                }
            }

            return null;

        }


        private void SetCookiesForBrowser(string url)
        {
            var collection = this.cookie.GetCookies(new Uri(url));
            foreach (Cookie c in collection)
            {
                InternetSetCookie(url, c.Name, c.Value);
            }

        }

        [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool InternetSetCookie(string lpszUrlName, string lbszCookieName, string lpszCookieData);

        #region Member Fields
        private CookieContainer cookie = new CookieContainer();
        public WebProxy proxy = null;
        public int Failure = 0;



#if DEBUG
        public static int TIMEOUT = 60000;
#else
         public static int TIMEOUT = 30000;
#endif


        public static int MAX_RETRY = 4;
        private const string USERAGENT = @"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)";
        #endregion



    }


    public class RequestStringBuilder
    {

        Dictionary<string, string> internalDictionary = new Dictionary<string, string>();

        /// <summary>
        /// duplicated key is not allowed, but no error with be thrown.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string this[string key]
        {
            get
            {
                if (internalDictionary.ContainsKey(key))
                    return internalDictionary[key];
                else
                    return string.Empty;
            }
            set
            {
                if (!internalDictionary.ContainsKey(key))
                    internalDictionary[key] = value;
                else
                    internalDictionary.Add(key, value);
            }
        }

        public string BuildString()
        {
            StringBuilder sb = new StringBuilder();
            foreach (var pair in internalDictionary)
            {
                sb.AppendFormat("{0}={1}&", pair.Key, pair.Value);
            }


            return sb.ToString().Trim('&');
        }

        public void Clear()
        {
            internalDictionary.Clear();
        }

        public override string ToString()
        {
            return BuildString();
        }

    }








}